import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import os
import csv
from PIL import Image, ImageTk
from main import generate_random_string, generate_qr_code, save_to_csv

# 软件信息常量
APP_NAME = "二维码生成器"
APP_VERSION = "1.0.0"
APP_AUTHOR = "陈良旭"
APP_COPYRIGHT = "©2025 chenliangxu68@163.com"
APP_DESCRIPTION = "一个简单易用的二维码生成工具，支持批量生成和自定义设置。"


class QRCodeGeneratorGUI:
    def __init__(self, root):
        self.root = root
        self.root.title(APP_NAME)
        self.root.geometry("800x600")
        
        # 创建菜单栏
        self.create_menu_bar()
        
        # 创建主框架
        main_frame = ttk.Frame(root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 创建左侧设置面板
        settings_frame = ttk.LabelFrame(main_frame, text="生成设置", padding="10")
        settings_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
        
        # 数量选择
        ttk.Label(settings_frame, text="生成数量:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.num_var = tk.StringVar(value="2")
        ttk.Entry(settings_frame, textvariable=self.num_var, width=20).grid(row=0, column=1, sticky=tk.W, pady=5)
        
        # 字符长度选择
        ttk.Label(settings_frame, text="字符长度:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.length_var = tk.StringVar(value="8")
        ttk.Entry(settings_frame, textvariable=self.length_var, width=20).grid(row=1, column=1, sticky=tk.W, pady=5)
        
        # 保存目录选择
        ttk.Label(settings_frame, text="保存目录:").grid(row=2, column=0, sticky=tk.W, pady=5)
        self.dir_var = tk.StringVar(value=os.getcwd())
        ttk.Entry(settings_frame, textvariable=self.dir_var, width=30).grid(row=2, column=1, sticky=tk.W, pady=5)
        ttk.Button(settings_frame, text="浏览", command=self.choose_directory).grid(row=2, column=2, sticky=tk.W, pady=5)
        
        # 生成按钮
        ttk.Button(settings_frame, text="生成二维码", command=self.generate_codes).grid(row=3, column=0, columnspan=3, pady=10)
        
        # 加载CSV按钮
        ttk.Button(settings_frame, text="加载现有CSV", command=self.load_existing_csv).grid(row=4, column=0, columnspan=3, pady=10)
        
        # 状态标签
        self.status_var = tk.StringVar()
        ttk.Label(settings_frame, textvariable=self.status_var).grid(row=5, column=0, columnspan=3, pady=5)
        
        # 创建右侧结果显示区域
        results_frame = ttk.LabelFrame(main_frame, text="生成结果", padding="10")
        results_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
        
        # 创建表格显示CSV内容
        self.tree = ttk.Treeview(results_frame, columns=('string', 'qr_code'), show='headings')
        self.tree.heading('string', text='随机字符串')
        self.tree.heading('qr_code', text='二维码文件路径')
        self.tree.column('string', width=150)
        self.tree.column('qr_code', width=250)
        self.tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(results_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=scrollbar.set)
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
        
        # 创建二维码图像显示区域
        self.qr_frame = ttk.LabelFrame(results_frame, text="二维码预览", padding="10")
        self.qr_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
        
        self.qr_image_label = ttk.Label(self.qr_frame)
        self.qr_image_label.pack(expand=True, fill=tk.BOTH)
        
        # 绑定选择事件和双击事件
        self.tree.bind('<<TreeviewSelect>>', self.show_selected_qr)
        self.tree.bind('<Double-1>', self.open_qr_image)
        
        # 创建右键菜单
        self.context_menu = tk.Menu(self.root, tearoff=0)
        self.context_menu.add_command(label="复制所有字符串", command=self.copy_all_strings)
        self.context_menu.add_command(label="复制选中字符串", command=self.copy_selected_strings)
        
        # 绑定右键菜单
        self.tree.bind("<Button-3>", self.show_context_menu)
        
        # 添加提示
        ttk.Label(results_frame, text="提示：双击表格行可在默认图像查看器中打开二维码").grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=5)
        ttk.Label(results_frame, text="提示：右键点击表格可复制字符串数据").grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=5)
        
        # 配置网格权重，使组件可以随窗口调整大小
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=3)
        main_frame.rowconfigure(0, weight=1)
        results_frame.columnconfigure(0, weight=1)
        results_frame.rowconfigure(0, weight=1)
        results_frame.rowconfigure(1, weight=1)

    def create_menu_bar(self):
        """创建菜单栏"""
        menu_bar = tk.Menu(self.root)
        self.root.config(menu=menu_bar)
        
        # 文件菜单
        file_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="退出", command=self.root.quit)
        
        # 帮助菜单
        help_menu = tk.Menu(menu_bar, tearoff=0)
        menu_bar.add_cascade(label="帮助", menu=help_menu)
        help_menu.add_command(label="使用说明", command=self.show_user_guide)
        help_menu.add_command(label="版本信息", command=self.show_version_info)
        help_menu.add_command(label="关于", command=self.show_about)
    
    def show_user_guide(self):
        """显示使用说明"""
        guide_window = tk.Toplevel(self.root)
        guide_window.title("使用说明")
        guide_window.geometry("600x400")
        guide_window.resizable(True, True)
        
        # 使用说明内容
        user_guide = """
二维码生成器使用说明

基本功能
1. **生成二维码**：输入要生成的二维码数量和字符串长度，点击"生成二维码"按钮。
2. **保存二维码**：二维码会自动保存到指定目录。
3. **导出CSV**：生成的随机字符串会自动保存为CSV文件。
4. **加载现有CSV**：可以加载已有的CSV文件，查看之前生成的二维码。

操作步骤
1. 在"生成数量"输入框中输入要生成的二维码数量。
2. 在"字符长度"输入框中输入随机字符串的长度。
3. 点击"浏览"按钮，选择保存二维码的目录。
4. 点击"生成二维码"按钮，生成指定数量的二维码。
5. 在右侧表格中查看生成的随机字符串和对应的二维码文件路径。
6. 点击表格中的行，可以在下方预览区域查看对应的二维码。
7. 双击表格中的行，可以在默认图像查看器中打开二维码图像。
8. 右键点击表格，可以复制所有或选中的字符串到剪贴板。

注意事项
- 生成的二维码图片格式为PNG。
- 导出的CSV文件包含所有生成的随机字符串和对应的二维码文件路径。
- 二维码图片的文件名为随机字符串。
- 可以通过"加载现有CSV"按钮，加载之前生成的二维码数据。
        """
        
        # 创建滚动文本框
        text_area = scrolledtext.ScrolledText(guide_window, wrap=tk.WORD, width=70, height=20)
        text_area.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
        text_area.insert(tk.END, user_guide)
        text_area.config(state=tk.DISABLED)  # 设置为只读
        
        # 关闭按钮
        close_button = ttk.Button(guide_window, text="关闭", command=guide_window.destroy)
        close_button.pack(pady=10)
    
    def show_version_info(self):
        """显示版本信息"""
        version_window = tk.Toplevel(self.root)
        version_window.title("版本信息")
        version_window.geometry("400x300")
        version_window.resizable(False, False)
        
        # 版本信息内容
        version_frame = ttk.Frame(version_window, padding="20")
        version_frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(version_frame, text=APP_NAME, font=("Arial", 16, "bold")).pack(pady=(0, 10))
        ttk.Label(version_frame, text=f"版本: {APP_VERSION}").pack(pady=5)
        ttk.Label(version_frame, text=APP_COPYRIGHT).pack(pady=5)
        
        # 版本历史
        history_frame = ttk.LabelFrame(version_frame, text="版本历史")
        history_frame.pack(pady=10, fill=tk.X)
        
        version_history = """
v1.0.0 (2025-07-01)
- 初始版本发布
- 支持批量生成二维码
- 支持导出CSV文件
- 支持自定义字符串长度
- 支持加载现有CSV文件
        """
        
        history_text = scrolledtext.ScrolledText(history_frame, wrap=tk.WORD, width=40, height=8)
        history_text.pack(padx=5, pady=5, fill=tk.BOTH)
        history_text.insert(tk.END, version_history)
        history_text.config(state=tk.DISABLED)  # 设置为只读
        
        # 关闭按钮
        close_button = ttk.Button(version_frame, text="关闭", command=version_window.destroy)
        close_button.pack(pady=10)
    
    def show_about(self):
        """显示关于信息"""
        about_window = tk.Toplevel(self.root)
        about_window.title("关于")
        about_window.geometry("400x300")
        about_window.resizable(False, False)
        
        # 关于信息内容
        about_frame = ttk.Frame(about_window, padding="20")
        about_frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(about_frame, text=APP_NAME, font=("Arial", 16, "bold")).pack(pady=(0, 10))
        ttk.Label(about_frame, text=APP_DESCRIPTION).pack(pady=10)
        ttk.Label(about_frame, text=f"开发者: {APP_AUTHOR}").pack(pady=5)

        # 联系信息
        contact_frame = ttk.LabelFrame(about_frame, text="联系方式")
        contact_frame.pack(pady=10, fill=tk.X)
        
        ttk.Label(contact_frame, text="技术支持: chenliangxu68@163.com").pack(pady=2)
        
        # 关闭按钮
        close_button = ttk.Button(about_frame, text="关闭", command=about_window.destroy)
        close_button.pack(pady=10)

    def choose_directory(self):
        dir_path = filedialog.askdirectory()
        if dir_path:
            self.dir_var.set(dir_path)
            
    def show_success_message(self, num_codes, save_dir):
        """显示成功消息，使用after方法避免阻塞用户操作"""
        # 创建一个临时的顶层窗口
        success_window = tk.Toplevel(self.root)
        success_window.title("成功")
        success_window.geometry("300x100")
        
        # 设置窗口样式
        success_window.transient(self.root)  # 设置为主窗口的临时窗口
        success_window.grab_set()  # 获取焦点
        
        # 添加消息
        message = f"已成功生成 {num_codes} 个二维码\n保存在: {save_dir}"
        ttk.Label(success_window, text=message, wraplength=280, justify='center').pack(expand=True, padx=10, pady=10)
        
        # 3秒后自动关闭
        success_window.after(3000, success_window.destroy)
        
        # 点击窗口任意位置关闭
        success_window.bind('<Button-1>', lambda e: success_window.destroy())
        
        # 将窗口居中显示
        success_window.update_idletasks()
        width = success_window.winfo_width()
        height = success_window.winfo_height()
        x = self.root.winfo_x() + (self.root.winfo_width() - width) // 2
        y = self.root.winfo_y() + (self.root.winfo_height() - height) // 2
        success_window.geometry(f"{width}x{height}+{x}+{y}")

    def load_existing_csv(self):
        """加载现有的CSV文件"""
        try:
            # 让用户选择CSV文件
            csv_file = filedialog.askopenfilename(
                title="选择CSV文件",
                filetypes=[("CSV文件", "*.csv")],
                initialdir=self.dir_var.get()
            )
            
            if not csv_file:  # 用户取消选择
                return
                
            # 获取CSV文件所在目录
            csv_dir = os.path.dirname(csv_file)
            csv_name = os.path.basename(csv_file)
            
            # 更新保存目录
            self.dir_var.set(csv_dir)
            
            # 加载CSV数据
            self.load_csv_data(csv_dir, csv_name)
            
            # 选择第一个条目
            if self.tree.get_children():
                first_item = self.tree.get_children()[0]
                self.tree.selection_set(first_item)
                self.show_selected_qr(None)
                
            self.status_var.set(f"已加载CSV文件：{csv_name}")
            
        except Exception as e:
            messagebox.showerror("错误", f"加载CSV文件时出错：{str(e)}")

    def show_selected_qr(self, event):
        """显示选中行对应的二维码"""
        selected_items = self.tree.selection()
        if not selected_items:
            return
            
        item = selected_items[0]
        qr_path = self.tree.item(item)['values'][1]
        save_dir = self.dir_var.get()
        full_path = os.path.join(save_dir, qr_path)
        
        try:
            # 加载并调整二维码图像大小
            image = Image.open(full_path)
            # 计算合适的显示大小，保持宽高比
            display_size = (200, 200)
            
            # 兼容不同版本的Pillow库
            try:
                image.thumbnail(display_size, Image.Resampling.LANCZOS)
            except AttributeError:
                # 旧版本Pillow
                image.thumbnail(display_size, Image.ANTIALIAS)
            
            # 转换为PhotoImage对象
            photo = ImageTk.PhotoImage(image)
            
            # 更新显示
            self.qr_image_label.configure(image=photo)
            self.qr_image_label.image = photo  # 保持引用以防止垃圾回收
            
            # 保存当前选中的图像路径
            self.current_qr_path = full_path
            
        except Exception as e:
            messagebox.showerror("错误", f"无法加载二维码图像：{str(e)}")
            
    def open_qr_image(self, event):
        """在默认图像查看器中打开二维码图像"""
        if hasattr(self, 'current_qr_path') and os.path.exists(self.current_qr_path):
            try:
                # 使用系统默认程序打开图像
                if os.name == 'nt':  # Windows
                    os.startfile(self.current_qr_path)
                elif os.name == 'posix':  # macOS, Linux
                    import subprocess
                    subprocess.call(('xdg-open', self.current_qr_path))
            except Exception as e:
                messagebox.showerror("错误", f"无法打开图像：{str(e)}")
            
    def load_csv_data(self, save_dir, csv_file='output.csv'):
        """加载CSV文件内容到表格中"""
        # 清空现有内容
        for item in self.tree.get_children():
            self.tree.delete(item)
            
        try:
            csv_path = os.path.join(save_dir, csv_file)
            with open(csv_path, 'r', encoding='utf-8') as f:
                reader = csv.DictReader(f)
                for row in reader:
                    self.tree.insert('', 'end', values=(row['string'], row['qr_code']))
        except Exception as e:
            messagebox.showerror("错误", f"无法加载CSV文件：{str(e)}")
            
    def show_context_menu(self, event):
        """显示右键菜单"""
        # 更新"复制选中字符串"菜单项的状态
        if not self.tree.selection():
            self.context_menu.entryconfig("复制选中字符串", state="disabled")
        else:
            self.context_menu.entryconfig("复制选中字符串", state="normal")
            
        # 在鼠标位置显示菜单
        self.context_menu.post(event.x_root, event.y_root)
        
    def copy_all_strings(self):
        """复制所有字符串到剪贴板"""
        try:
            # 获取所有行
            all_items = self.tree.get_children()
            if not all_items:
                messagebox.showinfo("提示", "表格中没有数据")
                return
                
            # 提取所有字符串
            strings = []
            for item in all_items:
                string_value = self.tree.item(item)['values'][0]
                strings.append(str(string_value))
                
            # 将字符串复制到剪贴板
            text = '\n'.join(strings)
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            
            # 显示成功消息
            self.status_var.set(f"已复制 {len(strings)} 个字符串到剪贴板")
            
        except Exception as e:
            messagebox.showerror("错误", f"复制失败：{str(e)}")
            
    def copy_selected_strings(self):
        """复制选中的字符串到剪贴板"""
        try:
            # 获取选中的行
            selected_items = self.tree.selection()
            if not selected_items:
                messagebox.showinfo("提示", "请先选择要复制的行")
                return
                
            # 提取选中的字符串
            strings = []
            for item in selected_items:
                string_value = self.tree.item(item)['values'][0]
                strings.append(str(string_value))
                
            # 将字符串复制到剪贴板
            text = '\n'.join(strings)
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            
            # 显示成功消息
            self.status_var.set(f"已复制 {len(strings)} 个字符串到剪贴板")
            
        except Exception as e:
            messagebox.showerror("错误", f"复制失败：{str(e)}")
            
    def generate_codes(self):
        try:
            num_codes = int(self.num_var.get())
            str_length = int(self.length_var.get())
            save_dir = self.dir_var.get()
            
            if num_codes <= 0 or str_length <= 0:
                messagebox.showerror("错误", "数量和长度必须大于0")
                return
                
            if not os.path.exists(save_dir):
                messagebox.showerror("错误", "保存目录不存在")
                return
            
            # 切换到保存目录
            original_dir = os.getcwd()
            os.chdir(save_dir)
            
            data = []
            self.status_var.set("正在生成...")
            self.root.update()
            
            # 清空表格现有内容
            for item in self.tree.get_children():
                self.tree.delete(item)
            
            for i in range(num_codes):
                random_string = generate_random_string(str_length)
                qr_code_buffer = generate_qr_code(random_string, f"{random_string}.png")
                
                # 保存二维码图片
                qr_code_path = f"{random_string}.png"
                with open(qr_code_path, 'wb') as f:
                    f.write(qr_code_buffer.getvalue())
                
                data.append({'string': random_string, 'qr_code': qr_code_path})
                
                # 实时更新表格
                self.tree.insert('', 'end', values=(random_string, qr_code_path))
                self.root.update()
            
            # 保存到CSV文件
            save_to_csv(data, 'output.csv')
            
            # 恢复原始工作目录
            os.chdir(original_dir)
            
            self.status_var.set(f"完成！生成了 {num_codes} 个二维码，保存在 {save_dir}")
            
            # 选择第一个生成的二维码显示
            if self.tree.get_children():
                first_item = self.tree.get_children()[0]
                self.tree.selection_set(first_item)
                self.show_selected_qr(None)
                
            # 显示成功消息，但不阻塞用户操作
            self.root.after(100, lambda: self.show_success_message(num_codes, save_dir))
            
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")
        except Exception as e:
            messagebox.showerror("错误", f"生成过程中出现错误：{str(e)}")
            self.status_var.set("生成失败")


def main():
    root = tk.Tk()
    app = QRCodeGeneratorGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()
